import java.util.Arrays;


public class MemoryManagementSystem {
	private String[] secondaryMemory;	//This array will hold the secondary memory
	private boolean useLRU; 				// true if LRU is used, false if FIFO is used

	private Page[] adressInMain;	
	
	private Page mainMemory;				//mainMemory, as a double linked list of Page objects
	private int mainMemorySize;				//main memory size capacity

	
	public MemoryManagementSystem(boolean useLRU) {
		secondaryMemory = new String[1000];	
		this.useLRU = useLRU;

		//SECONDARY memory init
		for(int i=0;i<secondaryMemory.length;i++)
		{
			secondaryMemory[i]="";
		}
		
		//adressInMain init
		adressInMain=new Page[1000];
				for(int i=0;i<adressInMain.length;i++)
				{
					adressInMain[i]=null;
				}
		
		//MAIN memory init
		mainMemorySize=50;
		mainMemory=new Page(secondaryMemory[0],0,null);
		
		Page thisPage=mainMemory;
		Page lastPage=mainMemory;
		adressInMain[0]=thisPage;
		
		for(int i=1;i<mainMemorySize;i++)
		{
			
			thisPage=new Page(secondaryMemory[i],i,lastPage);
			lastPage.setForward(thisPage);
			adressInMain[i]=thisPage;
			lastPage=thisPage;
		}
		
	    mainMemory.setBackward(thisPage);					//start of linked list point to its end via backward field
	    thisPage.setForward(mainMemory);					//end of linked list point to its start via forward field
		
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return "secondaryMemory=" + Arrays.toString(secondaryMemory);
	}
	
	//This method returns the data you read. Notice that this data is not used by our main, but you can use it for testing your code.
	
	public String read(int index) {
		
		Page page=PageIndexInMain(index);
		
		String text=page.getText();
		
		if(useLRU)
		{
			LRUchange(page);
		}
		
		return text;
	}

	public void write(int index, char c) {
		
		Page page=PageIndexInMain(index);
		
		String temp=page.getText()+c;
		page.setText(temp);
		
		if(useLRU)
		{
			LRUchange(page);
		}
		
	}
/**
 * 
 * @param key - wanted data index at secondaryMemory.
 * @return adress of Page with wanted data at mainMemory. incase this page doesnt exist in mainMemory, will insert it to 
 * 			mainMemory, and return oldest/least unused data to secondary memory.
 */
	private Page PageIndexInMain(int key)
	{
			Page adress=searchPageInMain(key);
			if(adress==null)
			{	
				adress=new Page(secondaryMemory[key],key,null);			//create a new page
				createPage(adress);										//enter new page to mainMemory list
			}	
			return adress;
	}
	
/**
 * search if data at secondaryMemory at index Key exist in mainMemory. 	
 * @param key - wanted data index at secondaryMemory.
 * @return if data exist in mainMemory, return the page adress in mainMemory. if not, return null.
 */
	private Page searchPageInMain(int key)
	{
		return adressInMain[key];
	}
/**
 * enter a new page to mainMemory, which contain data from an index in secondary memory.
 * and, put back main memory first page to secondary memory.
 * update mainMemory and adressInMain as well.	
 * @param newPage - the page to enter mainMemory
 */
	private void createPage(Page newPage)
	{
		//put back main memory first page (at mainMemory) to secondary memory
		Page old=mainMemory;
		secondaryMemory[old.getIndex()]=old.getText();		//sync data at secondary memory with up-to-date data at main memory
		
		
		//set mainMemory anew - remove the first page in list
		Page newMain=old.getForward();
		newMain.setBackward(old.getBackward());
		adressInMain[old.getIndex()]=null;					//remove first page adress from adressInMain, since it is no longer at main memory
		mainMemory=newMain;
		
		
		//insert new page
		newPage.setForward(mainMemory);
		newPage.setBackward(mainMemory.getBackward());
		adressInMain[newPage.getIndex()]=newPage;  				//write the adress of new page at adressInMain, so we know it exist in mainMemory and where
		
		//update first page (mainMemory) and last page in list so there backward and forward will point at newPage, respectably
		newPage.getBackward().setForward(newPage);
		mainMemory.setBackward(newPage);
		
	}

/**
 * to maintain LRU, every time a change accure, the page who changed will enter the list again at it end - without sync with secondaryMemory.
 * in case 	the first mainMemory page changed - his next page should be the new first mainMemory page (least updated page).
 * @param changedPage - the changed Page at mainMemory
 */
	private void LRUchange(Page changedPage)
	{
		//to maintain LRU, every time a change accur the page who changed will enter the list again - without sync with secondaryMemory
		
		//take the required page out, and order mainMemory anew
		Page backPage=changedPage.getBackward();
		Page nextPage=changedPage.getForward();
			backPage.setForward(nextPage);
			nextPage.setBackward(backPage);
		if(changedPage==mainMemory)			// if mainMemory is the page which moved - his next should be the new mainMemory (least updated page)
			mainMemory=nextPage;
		
		//insert changed page anew
			changedPage.setForward(mainMemory);
			changedPage.setBackward(mainMemory.getBackward());
			
		//update first page (mainMemory) and last page in list so there backward and forward will point at newPage, respectably
			changedPage.getBackward().setForward(changedPage);	
			mainMemory.setBackward(changedPage);
				
	}
}
